home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / winterp-1.13 / contrib / doc / rolo.el < prev    next >
Encoding:
Text File  |  1991-10-06  |  19.8 KB  |  505 lines

  1. ;;!emacs
  2. ;;
  3. ;; FILE:         rolo.el
  4. ;; SUMMARY:      Retrieves and sorts entries from a list of rolodex files
  5. ;; USAGE:        GNU Emacs Lisp Library
  6. ;;
  7. ;; AUTHOR:       Bob Weiner
  8. ;; ORG:          Motorola, Inc., Communications Sector, Applied Research
  9. ;; E-MAIL:       USENET:  weiner@novavax.UUCP
  10. ;;
  11. ;; ORIG-DATE:     7-Jun-89 at 22:08:29
  12. ;; LAST-MOD:     22-Feb-90 at 19:24:38 by Bob Weiner
  13. ;;
  14. ;; Copyright (C) 1991 Bob Weiner
  15. ;; WINTERP Copyright 1989, 1990, 1991 Hewlett-Packard Company (by Niels Mayer).
  16. ;; XLISP version 2.1, Copyright (c) 1989, by David Betz.
  17. ;;
  18. ;; Permission to use, copy, modify, distribute, and sell this software and
  19. ;; its documentation for any purpose is hereby granted without fee,
  20. ;; provided that the above copyright notice appear in all copies and that
  21. ;; both that copyright notice and this permission notice appear in
  22. ;; supporting documentation, and that the name of Hewlett-Packard, Niels
  23. ;; Mayer, Brown University and Bob Weiner not be used in advertising or
  24. ;; publicity pertaining to distribution of the software without specific,
  25. ;; written prior permission.  Hewlett-Packard, Niels Mayer, Brown University
  26. ;; and Bob Weiner makes no representations about the suitability of this
  27. ;; software for any purpose.  It is provided "as is" without express or
  28. ;; implied warranty.
  29. ;;
  30. ;; This file is not part of GNU Emacs.
  31. ;;
  32.  
  33. ;; This could use a key or field limited searching capability.
  34. ;;
  35. ;; DESCRIPTION:  
  36. ;;
  37. ;;  All I wanted to do was look up a phone number quickly . . .
  38. ;;
  39. ;;  FEATURES:
  40. ;;
  41. ;;   1.  Multiple rolodex files.
  42. ;;
  43. ;;   2.  Hierarchical rolodex entries as in:
  44. ;;        *    Company
  45. ;;        **     Manager
  46. ;;        ***      Underlings
  47. ;;
  48. ;;       Searching for Manager turns up all Underlings.  Searching for
  49. ;;       Company retrieves all listed employees.
  50. ;;
  51. ;;       This hierarchical system has proved very effective for retrieving
  52. ;;       computer system administration problem reports by vendor name,
  53. ;;       problem number or by subject area without having to resort to a
  54. ;;       database system.
  55. ;;
  56. ;;   4.  String and regular expression searching capabilities.  Ability to
  57. ;;       restrict number of matches or to report number of matches without
  58. ;;       displaying entries.
  59. ;;
  60. ;;   5.  Smart sorting of entries by hierarchy level.
  61. ;;
  62. ;;   See "rolo-logic.el" for logical search functions (and, or, not, xor).
  63. ;;
  64. ;;
  65. ;;  FOR NON-PROGRAMMERS:
  66. ;;
  67. ;;   Modify the second file entry in the definition of 'rolo-file-list'
  68. ;;   before using this package.
  69. ;;
  70. ;;   To add personal files to rolo-file-list--when you find these functions are
  71. ;;   useful for any sort of list lookup--add the following to your ~/.emacs
  72. ;;   file (substituting where you see <fileN>):
  73. ;;
  74. ;;      (require 'rolo)
  75. ;;      (setq rolo-file-list (append rolo-file-list '("<file1>" "<file2>")))
  76. ;;
  77. ;;   The only command you absolutely need that is defined here is
  78. ;;   'rolo-fgrep'; it locates any matching entries in a set of rolodex files.
  79. ;;   I recommend that you add the following key binding to one of your site
  80. ;;   specific Emacs initialization files:
  81. ;;
  82. ;;         (global-set-key "\C-x4r" 'rolo-fgrep)
  83. ;;
  84. ;;   Calling 'rolo-fgrep' with a prefix argument limits the number of matches
  85. ;;   to the specified number of entries.
  86. ;;
  87. ;;   The following commands are also provided:
  88. ;;     'rolo-grep' finds all entries matching a regular expression in a set
  89. ;;       of rolodex files;
  90. ;;     'rolo-edit' edits one's personal rolodex file;
  91. ;;     'rolo-sort' sorts all levels of entries in a rolodex file.
  92. ;;
  93. ;;   To make the 'rolo' library load whenever you initially call any of these
  94. ;;   functions, add the following to any of your Emacs init files:
  95. ;;
  96. ;;     (autoload 'rolo-fgrep "rolo"
  97. ;;       "Find entries in rolodex."
  98. ;;       t)     
  99. ;;     (autoload 'rolo-grep "rolo"
  100. ;;       "Find entries in rolodex."
  101. ;;       t)     
  102. ;;     (autoload 'rolo-edit "rolo"
  103. ;;       "Edit personal rolodex file."
  104. ;;       t)
  105. ;;     (autoload 'rolo-sort "rolo"
  106. ;;       "Sort rolodex file."
  107. ;;       t)
  108. ;;     
  109. ;;     
  110. ;;   Entries in rolodex files are separated by patterns matching
  111. ;;   'rolo-entry-regexp'.  Each entry may have any number of sub-entries
  112. ;;   which represent the next level down in the entry hierarchy.
  113. ;;   Sub-entries' separator patterns are always longer than their parents'.
  114. ;;   For example, if an entry began with '*' then its sub-entries would begin
  115. ;;   with '**' and so on.  Blank lines in rolodex files will not end up where
  116. ;;   you want them if you use the rolo-sort commands; therefore, blank lines
  117. ;;   are not recommended.
  118. ;;
  119. ;;   The reasons that the entries in 'rolo-file-list' have ".otl" suffixes
  120. ;;   are so that they do not conflict with file names that other rolodex
  121. ;;   programs might use and so that they are edited in 'outline-mode' by
  122. ;;   default.  If you want the latter behavior, uncomment and add something
  123. ;;   like the following to one of your GNU Emacs initialization files:
  124. ;;
  125. ;;     ;; Add to the list of suffixes that causes automatic mode invocation
  126. ;;     (setq auto-mode-alist
  127. ;;        (append '(("\\.otl$" . outline-mode)) auto-mode-alist))
  128. ;;
  129. ;;   The buffers containing the rolodex files are not killed after a search
  130. ;;   on the assumption that another search is likely to follow within this
  131. ;;   Emacs session.  You may wish to change this behavior with the following
  132. ;;   setting (after loading this file):
  133. ;;
  134. ;;     (setq rolo-kill-buffers-after-use t)
  135. ;;
  136. ;;   Here is a snippet from our group rolodex file (the ';'s should be
  137. ;;   removed of course and the '*'s should begin at the start of the line):
  138. ;;
  139. ;;=============================================================================
  140. ;;                  GROUP ROLODEX
  141. ;;    <Last Name>, <First Name>  <Co/Categ>   W<Work #>   H<Home #>  P<Pager #>
  142. ;;                          F<Fax #>    M<Modem #> C<Cellular #>
  143. ;;                          R<Other-radio #>
  144. ;;        <Address>       <Miscellaneous Info, Key Words>
  145. ;;=============================================================================
  146. ;;*   EX594, Digital-Systems-Research
  147. ;;**  Weiner, Bob          Motorola    W2087                 P7-7489
  148. ;;      FL19, L-1035
  149. ;;
  150. ;;
  151. ;;  FOR PROGRAMMERS:
  152. ;;
  153. ;;   If you change the value of 'rolo-entry-regexp', you will have to modify
  154. ;;   'rolo-sort'.
  155. ;;
  156. ;;   The following additional functions are provided:
  157. ;;     'rolo-sort-level' sorts a specific level of entries in a rolodex file;
  158. ;;     'rolo-map-level' runs a user specified function on a specific level of
  159. ;;       entries in a rolodex file;
  160. ;;     'rolo-fgrep-file', same as 'rolo-fgrep' but operates on a single file;
  161. ;;     'rolo-grep-file', same as 'rolo-grep' but operates on a single file;
  162. ;;     'rolo-display-matches', display last set of rolodex matches, if any;
  163. ;;     'rolo-toggle-narrow-to-entry' toggles between display of current entry
  164. ;;       and display of all matching entries.
  165. ;;
  166. ;;   This code works fine on properly formatted rolodex files but probably
  167. ;;   will fail on certain improperly formatted ones.
  168. ;;
  169. ;;
  170. ;;  MOD HISTORY:
  171. ;;
  172. ;;   12/17/89
  173. ;;     Added internal 'rolo-shrink-window' function for use in
  174. ;;     compressing/uncompressing the rolo view window to/from a size just
  175. ;;     large enough for the selected entry.  This is useful when a search
  176. ;;     turns up more entries than desired.
  177. ;;
  178. ;;   02/21/90
  179. ;;     Modified 'rolo-grep-file' and 'rolo-map-level' so they only set buffers
  180. ;;     read-only the first time they are read in.  This way, if someone edits a
  181. ;;     rolodex file and then does a rolo-fgrep or other function, the buffer
  182. ;;     will not be back in read-only mode.
  183. ;;
  184. ;;
  185. ;; DESCRIP-END.
  186.  
  187. (defconst rolo-file-list '("~/.rolodex.otl")
  188.   "List of files containing rolodex entries.
  189. The first file should be a user-specific rolodex file, typically in the home
  190. directory.  The second file is often a shared, group-specific rolodex file.
  191.  
  192. A rolo-file consists of:
  193.    (1) an optional header beginning with and ending with a line which matches
  194.        rolo-hdr-regexp;
  195.    (2) one or more rolodex entries beginning with rolo-entry-regexp, which
  196.        may be nested.")
  197.  
  198. (defconst rolo-kill-buffers-after-use nil
  199.   "Non-nil means kill rolodex file buffers after searching them for entries.
  200. Only unmodified buffers are killed.")
  201.  
  202. (defconst rolo-display-buffer "*Rolodex*"
  203.   "Buffer used to display set of last matching rolodex entries.")
  204.  
  205. (defconst rolo-entry-regexp "^\*+"
  206.   "Regular expression to match the beginning of a rolodex entry.
  207. This pattern must match the beginning of the line.  Entries may be nested
  208. through the use of increasingly longer beginning patterns.")
  209.  
  210. (defconst rolo-hdr-regexp "^==="
  211.   "Regular expression to match the first and last lines of rolodex file headers.
  212. This header is inserted into rolo-display-buffer before any entries from the
  213. file are added.")
  214.  
  215. (defun rolo-fgrep (string &optional max-matches rolo-file count-only)
  216.   "Display rolodex entries matching STRING, to a maximum of prefix arg MAX-MATCHES,
  217. in file(s) from optional ROLO-FILE or rolo-file-list.  Default is to find all
  218. matching entries.  Each entry is displayed with all of its sub-entries.
  219. Optional COUNT-ONLY non-nil means don't display matching entries.
  220. Returns number of entries matched.  See also documentation for the variable
  221. rolo-file-list."
  222.   (interactive "sRolodex string to match: \nP")
  223.   (let ((total-matches
  224.       (rolo-grep (regexp-quote string) max-matches rolo-file count-only)))
  225.     (if (interactive-p)
  226.     (message (concat (if (= total-matches 0) "No" total-matches)
  227.              " matching entr"
  228.              (if (= total-matches 1) "y" "ies")
  229.              " found in rolodex.")))
  230.     total-matches))
  231.  
  232. (defun rolo-grep (regexp &optional max-matches rolo-bufs count-only)
  233.   "Display rolodex entries matching REGEXP, to a maximum of prefix arg MAX-MATCHES,
  234. in buffer(s) from optional ROLO-BUFS or rolo-file-list.  Default is to find all
  235. matching entries.  Each entry is displayed with all of its sub-entries.
  236. Optional COUNT-ONLY non-nil means don't display matching entries.
  237. Returns number of entries matched.  See also documentation for the variable
  238. rolo-file-list."
  239.   (interactive "sRolodex regular expression to match: \nP")
  240.   (let ((rolo-file-list
  241.       (cond ((null rolo-bufs) rolo-file-list)
  242.         ((listp rolo-bufs) rolo-bufs)
  243.         ((list rolo-bufs))))
  244.     (obuf (current-buffer))
  245.     (display-buf (if count-only
  246.              nil
  247.                (set-buffer (get-buffer-create rolo-display-buffer))))
  248.     (total-matches 0))
  249.     (if count-only nil (setq buffer-read-only nil) (erase-buffer))
  250.     (mapcar '(lambda (file)
  251.            (if (or (null max-matches) (> max-matches 0))
  252.            (let ((num-matched
  253.                (rolo-grep-file file regexp max-matches count-only)))
  254.              (setq total-matches (+ total-matches num-matched))
  255.              (or (null max-matches)
  256.              (setq max-matches (- max-matches num-matched))))))
  257.         rolo-file-list)
  258.     (if (or count-only (= total-matches 0))
  259.     nil
  260.       (rolo-display-matches))
  261.     (if (interactive-p)
  262.     (message (concat (if (= total-matches 0) "No" total-matches)
  263.              " matching entr"
  264.              (if (= total-matches 1) "y" "ies")
  265.              " found in rolodex.")))
  266.     total-matches))
  267.  
  268. (defun rolo-edit ()
  269.   "Display personal rolodex file for editing."
  270.   (interactive)
  271.   (find-file (car rolo-file-list))
  272.   (setq buffer-read-only nil))
  273.  
  274. (defun rolo-sort (&optional rolo-file)
  275.   "Sort up to 14 levels of entries in ROLO-FILE (default is personal rolodex file).
  276. Uses default rolo-entry-regexp for sort.  Returns list of number of groupings
  277. at each entry level." 
  278.   (interactive "fRolodex file to sort: ")
  279.   (if (not rolo-file) (setq rolo-file (car rolo-file-list)))
  280.   (let ((level-regexp (regexp-quote "**************"))
  281.     (entries-per-level-list)
  282.     (n))
  283.     (while (not (equal level-regexp ""))
  284.       (setq n (rolo-sort-level rolo-file level-regexp))
  285.       (if (or (/= n 0) entries-per-level-list)
  286.       (setq entries-per-level-list
  287.         (append (list n) entries-per-level-list)))
  288.       (setq level-regexp (substring level-regexp 0 (- (length level-regexp) 2))))
  289.     entries-per-level-list))
  290.  
  291. (defun rolo-sort-level (rolo-file level-regexp &optional max-groupings)
  292.   "Sort groupings of entries in ROLO-FILE at hierarchy level given by LEVEL-REGEXP
  293. to a maximum of optional MAX-GROUPINGS.  Nil value of MAX-GROUPINGS means all
  294. groupings at the given level.  LEVEL-REGEXP should simply match the text of
  295. any rolodex entry of the given level, not the beginning of a line (^); an
  296. example, might be (regexp-quote \"**\") to match level two.  Returns number
  297. of groupings sorted."
  298.   (interactive "sRolodex file to sort: \nRegexp to match text of level's entries: \nP")
  299.   (rolo-map-level
  300.     '(lambda (start end) (sort-lines nil start end))
  301.     rolo-file
  302.     level-regexp
  303.     max-groupings))
  304.  
  305. (defun rolo-map-level (func rolo-buf level-regexp &optional max-groupings)
  306.   "Perform FUNC on each grouping of ROLO-BUF entries at hierarchy level LEVEL-REGEXP
  307. to a maximum of optional argument MAX-GROUPINGS.  Nil value of MAX-GROUPINGS
  308. means all groupings at the given level.  FUNC should take two arguments, the
  309. start and the end of the region that it should manipulate.  LEVEL-REGEXP
  310. should simply match the text of any rolodex entry of the given level, not the
  311. beginning of a line (^); an example, might be (regexp-quote \"**\") to match
  312. level two.  Returns number of groupings matched."
  313.   (let ((new-buf-p) (actual-buf))
  314.     (if (and (or (null max-groupings) (< 0 max-groupings))
  315.          (or (setq actual-buf (rolo-buffer-exists-p rolo-buf))
  316.          (if (file-exists-p rolo-buf)
  317.              (setq actual-buf (find-file-noselect rolo-buf t)
  318.                new-buf-p t))))
  319.     (let ((num-found 0)
  320.           (exact-level-regexp (concat "^\\(" level-regexp "\\)[ \t\n]"))
  321.           (outline-regexp rolo-entry-regexp)
  322.           (level-len))
  323.       ;; Load 'outline' library since its functions are used here.
  324.       (if (not (boundp 'outline-mode-map))
  325.           (load-library "outline"))
  326.       (set-buffer actual-buf)
  327.       (if new-buf-p (setq buffer-read-only t))
  328.       (goto-char (point-min))
  329.       ;; Pass buffer header if it exists
  330.       (if (re-search-forward rolo-hdr-regexp nil t 2)
  331.           (forward-line))
  332.       (while (and (or (null max-groupings) (< num-found max-groupings))
  333.               (re-search-forward exact-level-regexp nil t))
  334.         (setq num-found (1+ num-found))
  335.         (let* ((opoint (prog1 (point) (beginning-of-line)))
  336.            (grouping-start (point))
  337.            (start grouping-start)
  338.            (level-len (or level-len (- (1- opoint) start)))
  339.            (next-level-len)
  340.            (next-entry-exists)
  341.            (grouping-end)
  342.            (no-subtree))
  343.           (while (and (progn
  344.                 (if (setq next-entry-exists
  345.                       (re-search-forward rolo-entry-regexp nil t 2))
  346.                 (setq next-level-len (- (point)
  347.                             (progn (beginning-of-line)
  348.                                    (point)))
  349.                       grouping-end (< next-level-len level-len)
  350.                       no-subtree (<= next-level-len level-len))
  351.                   (setq grouping-end t no-subtree t)
  352.                   (goto-char (point-max)))
  353.                 (let ((end (point)))
  354.                   (goto-char start)
  355.                   (hide-subtree) ; And hide multiple lines of entry
  356.                   ;; Move to start of next entry at equal or higher level
  357.                   (setq start
  358.                     (if no-subtree
  359.                     end
  360.                       (if (re-search-forward rolo-entry-regexp
  361.                                  nil t)
  362.                       (progn (beginning-of-line) (point))
  363.                     (point-max))))
  364.                   ;; Remember last expression in 'progn' must always
  365.                   ;; return non-nil
  366.                   (goto-char start)))
  367.               (not grouping-end)))
  368.           (let ((end (point)))
  369.         (goto-char grouping-start)
  370.         (funcall func grouping-start end)
  371.         (goto-char end))))
  372.       (show-all)
  373.       (rolo-kill-buffer actual-buf)
  374.       num-found)
  375.       0)))
  376.  
  377. (defun rolo-fgrep-file (rolo-buf string &optional max-matches count-only)
  378.   "Retrieve entries in ROLO-BUF matching STRING to a maximum of optional MAX-MATCHES.
  379. Nil value of MAX-MATCHES means find all matches.  Optional COUNT-ONLY non-nil
  380. means don't retrieve matching entries.  Returns number of matching entries
  381. found."
  382.   (rolo-grep-file rolo-buf (regexp-quote string) max-matches count-only))
  383.  
  384. (defun rolo-grep-file (rolo-buf regexp &optional max-matches count-only)
  385.   "Retrieve entries in ROLO-BUF matching REGEXP to a maximum of optional MAX-MATCHES.
  386. Nil value of MAX-MATCHES means find all matches.  Optional COUNT-ONLY non-nil
  387. means don't retrieve matching entries.  Returns number of matching entries
  388. found."
  389.   (let ((new-buf-p) (actual-buf))
  390.     (if (and (or (null max-matches) (< 0 max-matches))
  391.          (or (setq actual-buf (rolo-buffer-exists-p rolo-buf))
  392.          (if (file-exists-p rolo-buf)
  393.              (setq actual-buf (find-file-noselect rolo-buf t)
  394.                new-buf-p t))))
  395.     (let ((hdr-pos) (num-found 0) (curr-entry-level))
  396.       (set-buffer actual-buf)
  397.       (if new-buf-p (setq buffer-read-only t))
  398.       (goto-char (point-min))
  399.       (if (re-search-forward rolo-hdr-regexp nil t 2)
  400.           (progn (forward-line)
  401.              (setq hdr-pos (cons (point-min) (point)))))
  402.       (re-search-forward rolo-entry-regexp nil t)
  403.       (while (and (or (null max-matches) (< num-found max-matches))
  404.               (re-search-forward regexp nil t))
  405.         (re-search-backward rolo-entry-regexp nil t)
  406.         (let ((start (point))
  407.           (next-entry-exists))
  408.           (re-search-forward rolo-entry-regexp nil t)
  409.           (rolo-to-entry-end
  410.         t (setq curr-entry-level (buffer-substring start (point))))
  411.           (or count-only
  412.           (and (= num-found 0) hdr-pos
  413.                (progn (append-to-buffer rolo-display-buffer
  414.                         (car hdr-pos) (cdr hdr-pos)))))
  415.           (setq num-found (1+ num-found))
  416.           (or count-only
  417.           (append-to-buffer rolo-display-buffer start (point)))))
  418.       (rolo-kill-buffer actual-buf)
  419.       num-found)
  420.       0)))
  421.  
  422. (defun rolo-display-matches ()
  423.   "Re-display buffer of previously found rolodex matches."
  424.   (interactive)
  425.   (let ((obuf (if (boundp 'obuf) obuf (current-buffer)))
  426.     (display-buf (if (boundp 'display-buf)
  427.              display-buf (get-buffer rolo-display-buffer))))
  428.     (pop-to-buffer display-buf)
  429.     (rolo-shrink-window)
  430.     (goto-char (point-min))
  431.     (set-buffer-modified-p nil)
  432.     (setq buffer-read-only t)
  433.     (let ((buf (get-buffer-window obuf)))
  434.     (if buf (select-window buf) (switch-to-buffer buf)))))
  435.  
  436. (defun rolo-toggle-narrow-to-entry ()
  437.   "Toggle between display of entry point is in and display of all matched entries."
  438.   (interactive)
  439.   (if (rolo-narrowed-p)
  440.       (widen)
  441.     (if (or (looking-at rolo-entry-regexp)
  442.         (re-search-backward rolo-entry-regexp nil t))
  443.     (progn (forward-char)
  444.            (narrow-to-region (1- (point)) (rolo-display-to-entry-end)))))
  445.   (rolo-shrink-window)
  446.   (goto-char (point-min)))
  447.  
  448. ;;
  449. ;; INTERNAL FUNCTIONS.
  450. ;;
  451.  
  452. (defun rolo-buffer-exists-p (rolo-buf)
  453.   "Returns buffer given by ROLO-BUF or nil.
  454. ROLO-BUF may be a file-name, buffer-name, or buffer."
  455.   (car (memq (get-buffer (or (and (stringp rolo-buf)
  456.                   (get-file-buffer rolo-buf))
  457.                  rolo-buf))
  458.          (buffer-list))))
  459.  
  460. (defun rolo-kill-buffer (rolo-buf)
  461.   (and rolo-kill-buffers-after-use (not (buffer-modified-p rolo-buf))
  462.        (kill-buffer rolo-buf)))
  463.  
  464. (defun rolo-to-entry-end (&optional include-sub-entries curr-entry-level)
  465. "Go to end of whole entry if optional INCLUDE-SUB-ENTRIES is non-nil.
  466. CURR-ENTRY-LEVEL is a string whose length is the same as the last found entry
  467. header.  If INCLUDE-SUB-ENTRIES is nil, CURR-ENTRY-LEVEL is not needed."
  468.   (while (and (setq next-entry-exists
  469.             (re-search-forward rolo-entry-regexp nil t))
  470.           include-sub-entries
  471.           (> (- (point) (save-excursion
  472.                   (beginning-of-line)
  473.                   (point)))
  474.          (length curr-entry-level))))
  475.   (if next-entry-exists
  476.       (progn (beginning-of-line) (point))
  477.     (goto-char (point-max))))
  478.  
  479. (defun rolo-display-to-entry-end ()
  480.   "Go to end of entry current entry, ignoring sub-entries."
  481.   (if (re-search-forward (concat rolo-hdr-regexp "\\|"
  482.                  rolo-entry-regexp) nil t)
  483.       (progn (beginning-of-line) (point))
  484.     (goto-char (point-max))))
  485.  
  486.       
  487. (defun rolo-shrink-window ()
  488.   (let* ((lines (count-lines (point-min) (point-max)))
  489.      (height (window-height))
  490.      (window-min-height 2)
  491.      (desired-shrinkage (1- (min (- height lines)))))
  492.     (and (>= lines 0)
  493.      (/= desired-shrinkage 0)
  494.      (> (screen-height) (1+ height))
  495.      (shrink-window 
  496.        (if (< desired-shrinkage 0)
  497.            (max desired-shrinkage (- height (/ (screen-height) 2)))
  498.   (min desired-shrinkage (- height window-min-height)))))))
  499.  
  500.  
  501. (defun rolo-narrowed-p ()
  502.   (or (/= (point-min) 1) (/= (1+ (buffer-size)) (point-max))))
  503.  
  504. (provide 'rolo)
  505.